/*
 * Copyright (c) 2012 Universidad Antonio de Nebrija.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0
 * which accompanies this distribution, and is available at
 * http://www.apache.org/licenses/LICENSE-2.0.html
 * 
 * Contributors:
 *    Universidad Antonio de Nebrija - initial API, implementation and documentation
 */
package com.nebrija.riif.parser;

import org.antlr.runtime.tree.Tree;

import com.nebrija.riif.model.ChildComponent;
import com.nebrija.riif.model.ChildComponentList;
import com.nebrija.riif.model.ChildComponentUnit;
import com.nebrija.riif.model.Component;
import com.nebrija.riif.model.Environment;
import com.nebrija.riif.model.FailMode;
import com.nebrija.riif.model.RIIFElement;
import com.nebrija.riif.model.RIIFProperties;
import com.nebrija.riif.model.RIIFPropertiesContainer;
import com.nebrija.riif.model.Timeline;

/**
 * Class to parse assign tokens and build the related object
 * @author asanchep
 *
 */
public class AssignProcessor implements RIIFParserProcessor {

	public static final String SEPARATOR = "'";
	public static final String NULL_PROPERTY = "NULL";
	
	@Override
	public RIIFElement apply(RIIFElement container, Tree t) {
		if(! (container instanceof Component || container instanceof Environment|| container instanceof Timeline)){
			return null;
		}
		
		// at least 2 elements are expected
		if(t.getChildCount()<2){
			return null;
		}
		// Get the referenced property and extract the name of the RIIFProperties object where it is stored
		// and the name of the property
		String id = t.getChild(0).getText();
		String name = parseRealContainerName(id);
		String property = parseRealName(id);
		
		// Get the value from the tree
		String value = parseExpression(t);

		//TODO: When working with arrays, we need to read the indexes where this assignment applies
		if (name==null || property==null){
			return null;
		}

		//Retrieve the RIIFProperties object where the property resides
		RIIFProperties target = null;
		if (container instanceof Component){
			target=((Component)container).getPropertiesElement(name);
		}else{
			target=(RIIFProperties)((RIIFPropertiesContainer)container).getElement(name);
		}
		
		// NULL Property is included in examples to identify when a fail mode does not affect the composite component
		if (target == null && name.equalsIgnoreCase(NULL_PROPERTY)){
			FailMode fm = new FailMode(name); //Create the fail_mode
			// Only components support failmodes
			((Component)container).addFailMode(fm); // Add it to the container 
			target = fm; // the target will be this new FailMode
		}
		
		if(target == null){
			// It is not a RIIFProperties, but a ChildComponent
			ChildComponent cc = ((Component)container).getChildComponent(name);
			if(cc==null){
				return null;
			}
			if(cc instanceof ChildComponentUnit){
				cc.setProperty(property, RIIFProperties.VALUE_KEY, value);
			}else{
				((ChildComponentList) cc).setProperty(property, RIIFProperties.VALUE_KEY, value);
			}
		}else{
			target.setProperty(property, value);
		}	
		return container;

	}
	
	protected String parseRealContainerName(String id){
		if(id.indexOf(SEPARATOR)==-1){
			return null;
		}
		return id.substring(0,id.indexOf(SEPARATOR));
	}
	
	
	protected String parseRealName(String id){
		if(id.indexOf(SEPARATOR)==-1){
			return id;
		}
		return id.substring(id.indexOf(SEPARATOR)+1);
	}

	/**
	 * Builds a string by parsing the property value from the tree
	 * @param t
	 * @return
	 */
	protected String parseExpression(Tree t){
		int children = t.getChildCount(); // 0-> id, 1->assign 2... value
		// There must be at least 1 child
		if (children<1){
			return null;
		}
		// Assign child where the value is stored (see rewrite rule in grammar for assign)
		Tree assignChild = t.getChild(1);
		// If the property is an array, ignore indices
		if(assignChild.getText().equals("[")){ //Array [ x : y ]
			assignChild = t.getChild(6);
		}
		// Assign may have several children with the value string
		children = assignChild.getChildCount();
		StringBuilder sb = new StringBuilder();
		for (int i=0;i<children;i++){
			sb.append(assignChild.getChild(i).getText());
		}
		return sb.toString();
	}
	
}
